use futures_util::stream::SplitSink;
use futures_util::stream::SplitStream;
use futures_util::{SinkExt, StreamExt};
use std::sync::Arc;
use tokio::net::TcpStream;
use tokio::sync::Mutex;
use tokio::sync::broadcast::{Receiver, Sender};
use tokio_tungstenite::WebSocketStream;
use tokio_tungstenite::accept_async;
use tungstenite::Message;
// 每个用户的单个连接会话
type Writer = Arc<Mutex<SplitSink<WebSocketStream<TcpStream>, Message>>>;
type Reader = Arc<Mutex<SplitStream<WebSocketStream<TcpStream>>>>;
#[derive(Debug, Clone)]
pub struct Session {
    pub modified_count: u64,
    pub key: String, // 密钥
    pub client_id: u64,
    pub ip: String,
    pub writer: Writer,
    pub reader: Reader,
}
// 用户信息
#[derive(Debug)]
pub struct User {
    pub id: u64,
    pub name: String,
    pub password: String,
    pub sessions: Vec<Session>,
}
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
pub struct SyncData {
    pub class : String,
    pub data : String,
    pub timestamp : i64,
}
impl User {
    pub fn new(id: u64, name: String, password: String, sessions: Vec<Session>) -> Self {
        Self {
            id,
            name,
            password,
            sessions,
        }
    }
    pub fn add_session(&mut self, session: Session) {
        self.sessions.push(session)
    }
    pub fn remove_session(&mut self, session: &Session) {
        self.sessions.retain(|s| s.client_id != session.client_id);
    }
}
impl std::fmt::Display for User {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "User {{ id: {}, name: {}, password: {}, sessions: [{}], }}",
            self.id,
            self.name,
            self.password,
            self.sessions.len()
        )
    }
}
impl Session {
    pub async fn new(client: TcpStream, client_id: u64, key: String) -> Self {
        let ip = client.peer_addr().unwrap().ip().to_string();
        println!("New connection from {}", ip);
        let ws_stream = accept_async(client).await.unwrap();
        let (write, read) = ws_stream.split();
        Self {
            modified_count: 0,
            key,
            client_id,
            ip,
            writer: Arc::new(Mutex::new(write)),
            reader: Arc::new(Mutex::new(read)),
        }
    }
    pub async fn start_loop(&mut self, mut rx: Receiver<Message>, tx: Arc<Sender<Message>>) {
        let mut reader = self.reader.lock().await;
        loop {
            tokio::select! {
                msg = async { match reader.next().await {
                    Some(Ok(msg)) => Ok(msg),
                    Some(Err(e)) => {
                        println!("Read error: {:?}", e);
                        Err(())
                    },
                    None => Err(())
                } } => match msg {
                        Ok(msg) => {
                            if msg.is_text() || msg.is_binary() {
                                let raw_data : SyncData = serde_json::from_str(&msg.to_string()).unwrap();
                                dispatch_message(raw_data.data).await;
                                if let Err(e) = tx.send(msg) {
                                    println!("Channel send error: {:?}", e);
                                    break;
                                }
                            }
                        },
                        Err(_) => {
                            println!("error");
                            break;
                        }
                    },
                Ok(msg) = rx.recv() => {
                    let mut writer = self.writer.lock().await;
                    let _ = writer.send(msg).await;
                    println!("向客户端发送消息")
                },
                // _ = tokio::time::sleep(Duration::from_secs(30)) => {
                //     println!("接收超时，主动关闭连接");
                //     break;
                // }
            }
        }
        println!("Connection closed");
    }
    pub fn update(&mut self) {}
}

async fn dispatch_message(msg: String) {
    println!("接收信息: \"{}\"", msg);
}
